<template>
  <v-page
    ref="pageRef"
    v-model="refreshing"
    background="#f5f6fa"
    header-background="white"
    padding-top="24rpx"
    :empty="state === 'finished' && !productList.length"
    empty-oss-src="empty-search.png"
    empty-text="没有找到相关商品"
    @refresh="refreshHandle"
    @scroll="scrollHandle"
    @tolower="getProductList"
  >
    <template #header>
      <v-navbar
        title="搜索"
        native
      />
      <view style="margin: 24rpx 24rpx 0">
        <search-input
          v-model="keyword"
          @search="searchHandle"
          @focus="searchFocusHandle"
          @blur="searchBlurHandle"
        />
      </view>
      <search-filter
        v-model:filter-type="filterType"
        v-model:filter-sort="filterSort"
        @update="refreshHandle"
        @select-category="isShowCategory = true"
      />
    </template>
    <template #default>
      <view class="product-list">
        <m-product-block
          v-for="(product, index) in productList"
          :key="index"
          :product="product"
          :display="index >= displayMinIndex && index < displayMaxIndex"
        />
      </view>
      <v-empty
        v-if="state === 'finished' && !productList.length"
        oss-src="empty-search.png"
        text="没有找到相关商品"
      />
      <v-loadmore
        :state="state"
        @reload="getProductList"
      />
    </template>
    <template #popup>
      <search-category-select-popup
        v-model:show="isShowCategory"
        @update:show="isShowCategory = $event"
        @select="selectCategoryHandle"
      />
    </template>
  </v-page>
</template>

<script setup lang="ts">
import SearchInput from './modules/search-input.vue';
import SearchFilter from './modules/search-filter.vue';
import searchCategorySelectPopup from './modules/search-category-select-popup.vue';
import type VPage from '@/components/v-page/v-page.vue';
import type { ProductItem } from './types';

const filterType = ref<'DEFAULT' | 'PRICE' | 'TIME'>('DEFAULT');
const filterSort = ref<'ASC' | 'DESC'>('ASC');

const keyword = ref('');
const selectingCategoryList = ref<CategoryVO[]>();

const searchHandle = async () => {
  pageIndex.value = 1;
  productList.value = [];
  state.value = undefined;
  selectingCategoryList.value = undefined;
  getProductList();
};
const searchFocusHandle = () => {
  if (selectingCategoryList.value) {
    keyword.value = '';
  }
};
const searchBlurHandle = () => {
  if (selectingCategoryList.value) {
    setCategoryKeyword();
  }
};

const isShowCategory = ref(false);
const selectCategoryHandle = (list: CategoryVO[]) => {
  pageIndex.value = 1;
  state.value = undefined;
  productList.value = [];
  selectingCategoryList.value = list;
  setCategoryKeyword();
  isShowCategory.value = false;
  getProductList();
};
const setCategoryKeyword = () => {
  if (!selectingCategoryList.value) return;
  keyword.value = selectingCategoryList.value
    .map((item) => item.name)
    .join(' / ');
};

onLoad(async () => {
  const pageStore = $store.page();
  await nextTick();
  const parameters = pageStore.parameters as {
    keyword?: string;
    category?: CategoryVO[];
  };
  if (parameters.category) {
    selectingCategoryList.value = parameters.category;
    setCategoryKeyword();
  } else {
    keyword.value = parameters.keyword ?? '';
  }
  getProductList();
});

const productList = ref<ProductItem[]>([]);
const pageIndex = ref(1);
const state = ref<'loading' | 'finished' | 'error'>();
const getProductList = async () => {
  if (selectingCategoryList.value) {
    await getProductListByCategory();
  } else {
    await getProductListByName();
  }
};
const getProductListByName = async () => {
  if (state.value !== undefined && state.value !== 'error') return;
  state.value = 'loading';
  const response = await $api.getSearchName({
    page_no: pageIndex.value,
    page_size: 20,
    name: keyword.value,
    sort: filterType.value,
    mode: filterSort.value
  });
  switch (response.__error) {
    case undefined:
      productList.value.push(...(response.data?.list ?? []));
      pageIndex.value++;
      if (productList.value.length >= response.data?.total) {
        state.value = 'finished';
      } else {
        state.value = undefined;
      }
      break;
    default:
      state.value = 'error';
  }
};
const getProductListByCategory = async () => {
  if (state.value !== undefined) return;
  state.value = 'loading';
  const selectingCategory =
    selectingCategoryList.value?.[selectingCategoryList.value.length - 1];
  const response = await $api.getSearchCategory({
    page_no: pageIndex.value,
    page_size: 20,
    sort: filterType.value,
    mode: filterSort.value,
    id: selectingCategory!.id
  });
  switch (response.__error) {
    case undefined:
      productList.value.push(...(response.data?.list ?? []));
      pageIndex.value++;
      if (productList.value.length >= response.data?.total) {
        state.value = 'finished';
      } else {
        state.value = undefined;
      }
      break;
    default:
      state.value = 'error';
  }
};

const refreshing = ref(false);
const refreshHandle = async () => {
  pageIndex.value = 1;
  productList.value = [];
  state.value = undefined;
  refreshing.value = true;
  await getProductList();
  refreshing.value = false;
};

const pageRef = ref() as Ref<InstanceType<typeof VPage>>;
const pageHeight = ref(0);
onMounted(async () => {
  pageHeight.value = await pageRef.value.getPageHeight(pageRef.value);
});
/** 虚拟列表逻辑 */
const rpxRatio = computed(() => {
  const system = uni.getSystemInfoSync();
  return 750 / system.windowWidth;
});
const scrollTop = ref(0);
const scrollHandle = (event: ScrollEvent) => {
  // 24rpx padding-top
  scrollTop.value = event.detail.scrollTop - 24 / rpxRatio.value;
};
/** 每行商品高度 */
const productBlockHeight = computed(() => {
  return rpxRatio.value ? 464 / rpxRatio.value : 0;
});
/** 最大可见行数 */
const maxDisplayLine = computed(() => {
  return pageHeight.value
    ? Math.ceil(pageHeight.value / productBlockHeight.value) + 2
    : 0;
});
/** 起始序号 */
const displayMinIndex = computed(() => {
  if (!productBlockHeight.value) return 0;
  const current = scrollTop.value;
  return Math.max(0, Math.floor(current / productBlockHeight.value) * 2);
});
/** 终止序号 */
const displayMaxIndex = computed(() => {
  if (!productBlockHeight.value) return 0;
  return displayMinIndex.value + maxDisplayLine.value * 2;
});
</script>

<style lang="scss" scoped>
.product-list {
  display: flex;
  flex-wrap: wrap;
  gap: 16rpx 0;
  padding: 0 24rpx;
  justify-content: space-between;
}
</style>
